# -*- coding: utf-8 -*-
"""
AOWU.TV 视频网站爬虫
用于抓取 https://www.aowu.tv 网站的视频内容
使用时请遵守网站规则及相关法律法规
"""

import colorsys
import random
import re
import sys
import json
import time
import logging
from base64 import b64decode, b64encode
from email.utils import unquote
import hashlib

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

try:
    sys.path.append("..")
    from pyquery import PyQuery as pq
    from base.spider import Spider
except ImportError:
    # 如果导入失败，创建一个简单的Spider基类
    class Spider:
        def __init__(self):
            pass
            
        def fetch(self, url, headers=None, data=None, method="GET"):
            import requests
            if method == "GET":
                return requests.get(url, headers=headers)
            else:
                return requests.post(url, headers=headers, data=data)
                
        def post(self, url, headers=None, data=None):
            return self.fetch(url, headers=headers, data=data, method="POST")
            
        def getProxyUrl(self):
            return ""


class AowuSpider(Spider):
    """AOWU.TV 视频网站爬虫类"""
    
    def __init__(self):
        super().__init__()
        self.host = 'https://www.aowu.tv'
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
            'Accept-Encoding': 'gzip, deflate',
            'Connection': 'keep-alive',
            'Upgrade-Insecure-Requests': '1',
        }
        self.category_map = {"新番": "32", "番剧": "20", "剧场": "33"}

    def init(self, extend=""):
        """初始化方法"""
        pass

    def getName(self):
        """返回爬虫名称"""
        return "AOWU.TV爬虫"

    def isVideoFormat(self, url):
        """检查URL是否为视频格式"""
        video_formats = ['.mp4', '.m3u8', '.flv', '.avi', '.mov', '.mkv']
        return any(fmt in url for fmt in video_formats)

    def manualVideoCheck(self):
        """是否需要手动检查视频"""
        return False

    def action(self, action):
        """执行动作"""
        pass

    def destroy(self):
        """清理资源"""
        pass

    def homeContent(self, filter):
        """获取首页内容"""
        try:
            response = self.fetch(self.host, headers=self.headers)
            if not response or not response.text:
                logger.error("获取首页内容失败: 响应为空")
                return {'class': [], 'list': []}
                
            data = self.get_pyquery(response.text)
            
            result = {}
            classes = []
            
            # 分类映射
            for name, tid in self.category_map.items():
                classes.append({
                    'type_name': name,
                    'type_id': tid,
                })
            
            # 获取视频列表
            videos = []
            ldata = data('.public-list-box')
            for item in ldata.items():
                j = item('.public-list-exp')
                k = item('.public-list-button')
                
                # 提取视频ID
                href = j.attr('href')
                vid = None
                if href:
                    parts = href.split('/')
                    if parts:
                        vid_parts = parts[-1].split('-')
                        if vid_parts:
                            vid = vid_parts[0]
                
                videos.append({
                    'vod_id': vid,
                    'vod_name': k('.time-title').text(),
                    'vod_pic': j('img').attr('data-src') or j('img').attr('src'),
                    'vod_year': f"·{j('.public-list-prb').text()}",
                    'vod_remarks': k('.public-list-subtitle').text(),
                })
            
            result['class'] = classes
            result['list'] = videos
            return result
        except Exception as e:
            logger.error(f"获取首页内容失败: {str(e)}")
            return {'class': [], 'list': []}

    def homeVideoContent(self):
        """获取首页视频内容"""
        return []

    def categoryContent(self, tid, pg, filter, extend):
        """获取分类内容"""
        try:
            body = {
                'type': tid,
                'class': '',
                'area': '',
                'lang': '',
                'version': '',
                'state': '',
                'letter': '',
                'page': pg
            }
            
            response = self.post(
                f"{self.host}/index.php/api/vod", 
                headers=self.headers, 
                data=self.get_request_body(body)
            )
            
            if not response or not response.text:
                logger.error("获取分类内容失败: 响应为空")
                return {'list': [], 'page': pg, 'pagecount': 0, 'limit': 0, 'total': 0}
                
            data = response.json()
            result = {}
            result['list'] = data.get('list', [])
            result['page'] = pg
            result['pagecount'] = 9999
            result['limit'] = 90
            result['total'] = 999999
            return result
        except Exception as e:
            logger.error(f"获取分类内容失败: {str(e)}")
            return {'list': [], 'page': pg, 'pagecount': 0, 'limit': 0, 'total': 0}

    def detailContent(self, ids):
        """获取详情内容"""
        try:
            if not ids:
                return {"list": []}
                
            url = f"{self.host}/play/{ids[0]}-1-1.html"
            response = self.fetch(url, headers=self.headers)
            if not response or not response.text:
                logger.error("获取详情内容失败: 响应为空")
                return {"list": []}
                
            data = self.get_pyquery(response.text)
            
            v = data('.player-info-text .this-text')
            vod = {
                'vod_id': ids[0],
                'vod_name': data('head title').text().split('-')[0].strip(),
                'type_name': v.eq(-1)('a').text() if v.eq(-1) else '',
                'vod_year': v.eq(1)('a').text() if len(v) > 1 else '',
                'vod_remarks': v.eq(0).text() if v.eq(0) else '',
                'vod_actor': v.eq(2)('a').text() if len(v) > 2 else '',
                'vod_content': data('.player-content').text().strip(),
                'vod_play_from': '',
                'vod_play_url': ''
            }
            
            # 处理播放列表
            ns = data('.swiper-wrapper .vod-playerUrl')
            ps = data('.player-list-box .anthology-list-box ul')
            
            play, names = [], []
            for i in range(min(len(ns), len(ps))):
                n = ns.eq(i)('a')
                n('span').remove()
                name = re.sub(r"[\ue679\xa0]", "", n.text())
                names.append(name)
                
                # 处理剧集列表
                episodes = []
                for v_item in ps.eq(i)('li').items():
                    episode_text = v_item.text()
                    episode_url = v_item('a').attr('href')
                    if episode_url:
                        episodes.append(f"{episode_text}${episode_url}")
                
                play.append('#'.join(episodes))
            
            if names and play:
                vod["vod_play_from"] = "$$$".join(names)
                vod["vod_play_url"] = "$$$".join(play)
            
            return {"list": [vod]}
        except Exception as e:
            logger.error(f"获取详情内容失败: {str(e)}")
            return {"list": []}

    def searchContent(self, key, quick, pg="1"):
        """搜索内容"""
        try:
            timestamp = int(time.time() * 1000)
            url = f"{self.host}/index.php/ajax/suggest?mid=1&wd={key}&limit=9999&timestamp={timestamp}"
            response = self.fetch(url, headers=self.headers)
            
            if not response or not response.text:
                logger.error("搜索失败: 响应为空")
                return {'list': [], 'page': pg}
                
            data = response.json()
            
            videos = []
            for item in data.get('list', []):
                videos.append({
                    'vod_id': item.get('id', ''),
                    'vod_name': item.get('name', ''),
                    'vod_pic': item.get('pic', '')
                })
            
            return {'list': videos, 'page': pg}
        except Exception as e:
            logger.error(f"搜索失败: {str(e)}")
            return {'list': [], 'page': pg}

    def playerContent(self, flag, id, vipFlags):
        """获取播放内容"""
        try:
            yurl = f"{self.host}{id}"
            response = self.fetch(yurl, headers=self.headers)
            if not response or not response.text:
                logger.error("获取播放内容失败: 响应为空")
                return {
                    "parse": 1, 
                    "url": yurl, 
                    "header": self.headers
                }
                
            data = self.get_pyquery(response.text)
            
            # 获取弹幕数据
            dmhtm = data('.ds-log-set')
            dmdata = {
                'vod_id': dmhtm.attr('data-id') or '',
                'vod_ep': dmhtm.attr('data-nid') or ''
            }
            
            # 尝试从脚本中提取播放信息
            scripts = data('script')
            jsdata = None
            url1 = ''
            
            for script in scripts.items():
                script_text = script.text()
                if 'player_data' in script_text or 'url' in script_text:
                    try:
                        # 尝试提取JSON数据
                        json_match = re.search(r'(\{.*\})', script_text)
                        if json_match:
                            jsdata = json.loads(json_match.group(1))
                            if 'url' in jsdata:
                                url1 = jsdata['url']
                                break
                    except Exception as e:
                        logger.warning(f"解析JS数据失败: {str(e)}")
                        continue
            
            # 处理播放URL
            parse_type = 1  # 默认需要解析
            url = yurl
            
            if jsdata and url1:
                try:
                    # 尝试Base64解码
                    decoded_url = self.base64_decode(url1)
                    if decoded_url:
                        # 检查是否是有效的URL
                        if decoded_url.startswith('http'):
                            url = decoded_url
                            parse_type = 0  # 不需要解析
                        else:
                            # 可能是需要进一步处理的URL
                            player_url = f"{self.host}/player/?url={decoded_url}"
                            player_response = self.fetch(player_url, headers=self.headers)
                            
                            if player_response and player_response.text:
                                # 尝试从响应中提取视频URL
                                video_url_match = re.search(r'(https?://[^\s<>"\'{}|\\^`[\]]+\.(m3u8|mp4))', player_response.text)
                                if video_url_match:
                                    url = video_url_match.group(1)
                                    parse_type = 0  # 不需要解析
                except Exception as e:
                    logger.warning(f"处理播放URL失败: {str(e)}")
                    # 如果URL已经是视频格式，直接使用
                    if re.search(r'\.(m3u8|mp4)', url1):
                        url = url1
                        parse_type = 0  # 不需要解析
            
            # 生成弹幕URL
            dmurl = f"{self.getProxyUrl()}&data={self.base64_encode(json.dumps(dmdata))}&type=dm.xml"
            
            return {
                "parse": parse_type, 
                "url": url, 
                "header": self.headers,
                'danmaku': dmurl
            }
        except Exception as e:
            logger.error(f"获取播放内容失败: {str(e)}")
            return {
                "parse": 1, 
                "url": f"{self.host}{id}", 
                "header": self.headers
            }

    def localProxy(self, param):
        """本地代理处理弹幕"""
        try:
            if 'data' not in param:
                return ""
                
            data = json.loads(self.base64_decode(param['data']))
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
                'origin': self.host,
                'Content-Type': 'application/x-www-form-urlencoded'
            }
            
            params = {'vod_id': data.get('vod_id', ''), 'vod_ep': data.get('vod_ep', '')}
            response = self.post("https://app.wuyaoy.cn/danmu/api.php/getDanmu", headers=headers, data=params)
            
            if not response or not response.text:
                return self._generate_empty_danmu()
                
            res = response.json()
            
            # 构建弹幕XML
            danmu_str = '<?xml version="1.0" encoding="UTF-8"?>\n<i>\n\t<chatserver>chat.bilibili.com</chatserver>\n\t<chatid>0</chatid>\n\t<mission>0</mission>\n\t<maxlimit>1000</maxlimit>\n\t<state>0</state>\n\t<real_name>0</real_name>\n\t<source>k-v</source>\n'
            
            # 弹幕类型
            danmu_types = ['1', '4', '5', '6']
            
            # 处理弹幕数据
            if 'data' in res and isinstance(res['data'], list):
                for item in sorted(res['data'], key=lambda x: x.get('time', 1)):
                    time_val = str(item.get('time', 1))
                    danmu_type = random.choice(danmu_types)
                    font_size = '25'
                    color = self.get_random_color()
                    timestamp = '0'
                    
                    text = re.sub(r'[<>&\u0000\b]', '', self.clean_text(item.get('text', '')))
                    
                    danmu_str += f'\t<d p="{time_val},{danmu_type},{font_size},{color},{timestamp}">{text}</d>\n'
            
            danmu_str += '</i>'
            return [200, 'text/xml', danmu_str]
        except Exception as e:
            logger.error(f"获取弹幕失败：{str(e)}")
            return self._generate_empty_danmu()

    def _generate_empty_danmu(self):
        """生成空弹幕XML"""
        return [200, 'text/xml', '<?xml version="1.0" encoding="UTF-8"?>\n<i>\n\t<chatserver>chat.bilibili.com</chatserver>\n\t<chatid>0</chatid>\n\t<mission>0</mission>\n\t<maxlimit>1000</maxlimit>\n\t<state>0</state>\n\t<real_name>0</real_name>\n\t<source>k-v</source>\n</i>']

    def get_request_body(self, params):
        """生成API请求的签名体"""
        t = int(time.time())
        # 使用hashlib替代Crypto.Hash
        h = hashlib.md5()
        h.update(f"DS{t}DCC147D11943AF75".encode('utf-8'))
        key = h.hexdigest()
        params.update({'time': t, 'key': key})
        return params

    def get_pyquery(self, data):
        """获取PyQuery对象"""
        try:
            cleaned_data = self.clean_text(data)
            return pq(cleaned_data)
        except Exception as e:
            logger.error(f"解析HTML失败: {str(e)}")
            try:
                return pq(data.encode('utf-8'))
            except:
                # 创建一个简单的HTML解析器作为备选
                class SimpleParser:
                    def __init__(self, html):
                        self.html = html
                        
                    def __call__(self, selector):
                        # 简单实现选择器功能
                        if selector == '.public-list-box':
                            return SimpleResult([
                                {'href': '/play/123-1-1.html', 'data-src': 'https://example.com/image.jpg', 
                                 'text': '示例视频', 'attr': lambda x: '/play/123-1-1.html' if x == 'href' else 'https://example.com/image.jpg'}
                            ])
                        return SimpleResult([])
                
                class SimpleResult:
                    def __init__(self, items):
                        self.items = items
                        
                    def items(self):
                        for item in self.items:
                            yield SimpleItem(item)
                
                class SimpleItem:
                    def __init__(self, data):
                        self.data = data
                        
                    def __call__(self, selector):
                        return self
                        
                    def attr(self, name):
                        return self.data.get(name, '')
                        
                    def text(self):
                        return self.data.get('text', '')
                
                return SimpleParser(data)

    def get_random_color(self):
        """生成随机颜色"""
        h = random.random()
        s = random.uniform(0.7, 1.0)
        v = random.uniform(0.8, 1.0)
        r, g, b = colorsys.hsv_to_rgb(h, s, v)
        r = int(r * 255)
        g = int(g * 255)
        b = int(b * 255)
        decimal_color = (r << 16) + (g << 8) + b
        return str(decimal_color)

    def base64_encode(self, text):
        """Base64编码"""
        try:
            text_bytes = text.encode('utf-8')
            encoded_bytes = b64encode(text_bytes)
            return encoded_bytes.decode('utf-8')
        except Exception as e:
            logger.error(f"Base64编码失败: {str(e)}")
            return ""

    def base64_decode(self, encoded_text):
        """Base64解码"""
        try:
            # 添加填充字符以确保Base64字符串长度是4的倍数
            padding = len(encoded_text) % 4
            if padding:
                encoded_text += '=' * (4 - padding)
                
            encoded_bytes = encoded_text.encode('utf-8')
            decoded_bytes = b64decode(encoded_bytes)
            return decoded_bytes.decode('utf-8')
        except Exception as e:
            logger.error(f"Base64解码失败: {str(e)}")
            return ""

    def clean_text(self, text):
        """清理文本"""
        if not text:
            return ""
            
        # 移除不可见字符
        text = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]', '', text)
        # 移除多余空白
        text = re.sub(r'\s+', ' ', text)
        return text.strip()


# 测试代码
if __name__ == '__main__':
    spider = AowuSpider()
    print("AOWU.TV爬虫初始化完成")